
package bibliotheque.document;

import bibliotheque.transaction.TransactionId;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Semaphore;
import java.util.Hashtable;
import java.util.Iterator;


public class Document {
    
    private class TimertaskMonopolyTimeout extends TimerTask{
        
        private TransactionId id;
        
        public TimertaskMonopolyTimeout(TransactionId id){
            super();
            this.id = id;
        }
        
        public void run(){
            try {
                Free(this.id);
            } catch (Exception ex) {
                
            }
        }
    }
    
    public enum State {FREE, CONSULTING,READING, REMOVED};
    
    private Semaphore lock1;
    private Semaphore lock2;
    private Semaphore lock3;
    
    private Hashtable<TransactionId, TimertaskMonopolyTimeout> hash;
    private static long MONOPOLY_TIMEOUT = 300000; //non monopolisation 5 minutes
    
    private State state;
    private DocumentSummary summary;
    private String contents;
    private Timer timer;
    
    public Document(DocumentId id, String title, String summary, String contents) {
        this.summary = new DocumentSummary(id, title, summary);
        this.state = State.FREE;
        this.lock1 = new Semaphore(1);
        this.lock2 = new Semaphore(1);
        this.lock3 = new Semaphore(1);
        this.hash = new Hashtable<TransactionId, TimertaskMonopolyTimeout>();
        this.contents = contents;
        this.timer = new Timer();
    }
    
    public boolean Take(TransactionId id){
        boolean res = true;
        this.lock1.acquireUninterruptibly();
        this.lock2.acquireUninterruptibly();
        this.lock3.acquireUninterruptibly();
        if(this.state == State.REMOVED) {
            this.lock2.release();
            this.lock1.release();
            res = false;
        } else {
            this.state = State.READING;
            TimertaskMonopolyTimeout task = new TimertaskMonopolyTimeout(id);
            this.timer.schedule(task, this.MONOPOLY_TIMEOUT);
            this.hash.put(id,task);
        }
        
        this.lock3.release();
        return(res);
    }
    
    
    
    public boolean Consult(TransactionId id){
        boolean res = true;
        this.lock1.acquireUninterruptibly();
        this.lock2.tryAcquire();
        this.lock3.acquireUninterruptibly();
        if(this.state == State.REMOVED) {
            this.lock2.release();
            res = false;
        } else {
            this.state = State.CONSULTING;
            TimertaskMonopolyTimeout task = new TimertaskMonopolyTimeout(id);
            this.timer.schedule(task, this.MONOPOLY_TIMEOUT);
            this.hash.put(id,task);
        }
        this.lock3.release();
        this.lock1.release();
        return(res);
    }
    
    public void Free(TransactionId id) throws Exception {
        try {
            this.lock3.acquireUninterruptibly();
            if (!this.hash.containsKey(id)) throw new Exception("Free failed cause the transaction does not own the document.");
            this.hash.get(id).cancel();
            this.hash.remove(id);
            if (this.state == State.READING) this.lock1.release();
            if(this.hash.size() == 0) {
                this.state = State.FREE;
                this.lock2.release();
            }
        } finally {
            this.lock3.release();
        }
    }
    
    public void FreeAndRemove(TransactionId id) throws Exception {
        try {
            this.lock3.acquireUninterruptibly();
            if (this.state != State.READING) throw new Exception("FreeAndRemove failed cause the transaction state is not reading.");
            if (!this.hash.containsKey(id)) throw new Exception("FreeAndRemove failed cause the transaction does not own the document.");
            this.hash.remove(id);
            this.state = State.REMOVED;
            this.lock1.release();
            this.lock2.release();
        } finally {
            this.lock3.release();
        }
    }
    
    public void PreFreeAndRemove(TransactionId tid) throws Exception{
        this.lock3.acquireUninterruptibly();
        if (this.state != State.READING) throw new Exception("FreeAndRemove failed cause the transaction state is not reading.");
        if (!this.hash.containsKey(tid)) throw new Exception("FreeAndRemove failed cause the transaction does not own the document.");
        this.hash.get(tid).cancel();
        this.lock3.release();
    }
    
    public DocumentSummary GetDocSummary(){
        return this.summary;
    }
    
    public String GetContents(TransactionId id) throws Exception {
        try {
            this.lock3.acquireUninterruptibly();
            if (!this.hash.containsKey(id)) throw new Exception("GetContents failed cause the transaction does not own the document.");
            return this.contents;
        } finally {
            this.lock3.release();
        }
    }
    
    public boolean MatchFilter(ArrayList<String> filter) {
        String word;
        Iterator<String> it = filter.iterator();
        boolean match = false;
        String[] title = this.GetDocSummary().GetTitle().split(" ");
        String[] summary = this.GetDocSummary().GetSummary().split(" ");
        while (!match && it.hasNext()) {
            word = it.next();
            for(int i = 0; i<title.length ; i++) {
                match |= word.equals(title[i]);
            }
            for(int i = 0; i<summary.length ; i++) {
                match |= word.equals(summary[i]);
            }
        }
        return(match);
    }
   
    public State GetState(){
        return this.state;
    }
}
